﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace Lib
{
    public class Rules
    {
        public List<TransactionValue> antecedent { get; set; } // tien de
        public List<TransactionValue> consequent { get; set; } // ket qua

        public double MinConf;
        public double Minsupp;
        public List<FrequentItem> FI = new List<FrequentItem>();
        public List<string> AssiationRules = new List<string>();
        public TransactionData Data = new TransactionData();

        public double Minsup;
        public Rules(TransactionData data, List<FrequentItem> fi, double minconf)
        {
            Data = data;
            FI = fi.OrderBy(x => x.Name.Count).ToList();
            MinConf = minconf;
        }


        /// <summary>
        /// Thuật toán Simple: tạo ra tất cả các luật hợp lệ có dạng ã ⇒(l_k - ã),∀ ã ⊂ a_m   
        /// </summary>
        /// <param name="l">l_k</param>
        /// <param name="a">a_m</param>
        public void GenRules(FrequentItem l, FrequentItem a)
        {
            List<List<TransactionValue>> A = GetSubFi(a.Name).FindAll(x=>x.Count == a.Name.Count -1).ToList();
            foreach (var antecedent in A)
            {
                FrequentItem Antecedent = new FrequentItem();
                var ece = FI.FindAll(x => x.Name.Count == antecedent.Count);
                foreach (var item in ece)
                {
                    if (CheckSubset(item.Name, antecedent))
                    {
                        Antecedent = new FrequentItem { Name = item.Name, Supp = item.Supp };
                        break;
                    }
                }
                //Antecedent = FI.Find(x => x.fi.Except(antecedent).ToList().Count == 0);
                if (Antecedent.Name != null)
                {
                    List<TransactionValue> consequent = l.Name.Except(antecedent).ToList();
                    double conf = l.Supp / Antecedent.Supp;
                    if (conf >= MinConf)
                    {
                        string antce = "";
                        antecedent.ForEach(x => antce += x.AttName + ": " + x.Name + "   ");
                        string conse = "";
                        consequent.ForEach(x => conse += x.AttName + ": " + x.Name + "   ");
                        string r = String.Format("{0}=> {1} conf = {2} ", antce, conse, conf);
                        if (AssiationRules.Find(x => x == r) == null)
                        {
                            AssiationRules.Add(r);
                        }
                        if (antecedent.Count > 1)
                            GenRules(l, Antecedent);
                    }
                }
            }
        }
        /// <summary>
        /// Sinh cac tap con cua 1 tap pho bien
        /// </summary>
        /// <param name="fi"></param>
        /// <returns></returns>
        private List<List<TransactionValue>> GetSubFi(List<TransactionValue> fi)
        {
            List<List<TransactionValue>> result = new List<List<TransactionValue>>();
            int subsetCount = (int)Math.Pow(2, fi.Count());
            for (int i = 0; i < subsetCount; i++)
            {
                List<TransactionValue> subset = new List<TransactionValue>();
                for (int bitIndex = 0; bitIndex < fi.Count; bitIndex++)
                {
                    if (GetBit(i, bitIndex) == 1)
                    {
                        subset.Add(fi[bitIndex]);
                    }
                }
                if (subset.Count != 0)// && subset.Count == fi.Count - 1)
                    result.Add(subset);
            }
            return result;

        }

        private int GetBit(int value, int position)
        {
            int bit = value & (int)Math.Pow(2, position);
            return (bit > 0 ? 1 : 0);
        }


        // Faster
        public void Ap_GenRules(FrequentItem l, Dictionary<List<TransactionValue>, double> h)
        {
            Apriori ap = new Apriori(Data, Minsup);
            var num = h.Keys.First().Count;
            if (l.Name.Count > num + 1)
            {
                Dictionary<List<TransactionValue>, double> H1 = ap.Apriori_Gen(h);
                List<List<TransactionValue>> a = H1.Keys.ToList();
                for (int i = 0; i < a.Count; i++)
                {
                    consequent = a[i];
                    antecedent = l.Name.Except(consequent).ToList();
                    FrequentItem Item = FI.Find(x => x.Name.Except(antecedent).ToList().Count == 0);
                    double conf = l.Supp / Item.Supp;
                    if (conf >= MinConf)
                    {
                        string antce = "";
                        antecedent.ForEach(x => antce += x.AttName + ": " + x.Name + "   ");
                        string conse = "";
                        consequent.ForEach(x => conse += x.AttName + ": " + x.Name + "   ");
                        string r = String.Format("{0}=> {1} \tconf = {2}", antce, conse, conf);
                        AssiationRules.Add(r);
                    }
                    else
                    {
                        a.Remove(consequent);
                        i--;
                    }
                }
                Ap_GenRules(l, H1);
            }
        }


        public Dictionary<List<TransactionValue>, double> GetConsequent(FrequentItem l, FrequentItem a)
        {
            Dictionary<List<TransactionValue>, double> Consequent = new Dictionary<List<TransactionValue>, double>();
            List<List<TransactionValue>> A = GetSubFi(a.Name);
            foreach (var sub in A)
            {
                FrequentItem M = FI.Find(x => CheckSubset(x.Name, sub) && sub.Count == x.Name.Count);//.fi.Except(sub).ToList().Count == 0);
                double conf = l.Supp / M.Supp;
                if (conf >= MinConf)
                {
                    consequent = l.Name.Except(sub).ToList();
                    double supp = FI.Find(x => x.Name.Except(consequent).ToList().Count == 0).Supp;
                    Consequent.Add(consequent, supp);
                }
            }

            return Consequent;
        }



        /// <summary>
        /// hàm phát sinh luật truyền thống
        /// </summary>

        public void Extract_Ar(List<FrequentItem> frequents)
        {
            AssiationRules = new List<string>();
            foreach (var fi in frequents)
            {
                if (fi.Name.Count >= 2)
                {
                    int index = frequents.FindIndex(x => x == fi);
                    for (int i = 0; i < index; i++)
                    {
                        if (frequents[i].Name.Except(fi.Name).ToList().Count == 0 && fi.Name.Except(frequents[i].Name).ToList().Count == 1)
                        {
                            double conf = fi.Supp / frequents[i].Supp;
                            if (conf >= MinConf)
                            {
                                string a = "";
                                frequents[i].Name.ForEach(x => a += x.AttName + ": " + x.Name + "   ");
                                string b = "";
                                fi.Name.Except(frequents[i].Name).ToList().ForEach(x => b += x.AttName + ": " + x.Name + "   ");
                                string r = String.Format("{0} => {1}  conf = {2}", a, b, conf);
                                AssiationRules.Add(r);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Sinh tập luật có vế phải 1-itemset.
        /// </summary>
        /// <param name="frequents"></param>
        public void Simple_Ar(List<FrequentItem> frequents)
        {
            AssiationRules = new List<string>();
            foreach (var fi in frequents.FindAll(x => x.Name.Count >= 2))
            {
                foreach (var conse in frequents.FindAll(x => x.Name.Count == 1))
                {
                    if (CheckSubset(conse.Name, fi.Name))
                    {
                        List<TransactionValue> an = fi.Name.Except(conse.Name).ToList();
                        var anteName = Getfi(fi, conse.Name);
                        var ante = FindFI(anteName, frequents);

                        double confi = fi.Supp / ante.Supp;
                        if (confi >= MinConf)
                        {
                            string a = "";
                            ante.Name.ForEach(x => a += x.AttName + ": " + x.Name + "   ");
                            string b = "";
                            conse.Name.ForEach(x => b += x.AttName + ": " + x.Name + "   ");
                            string r = String.Format("{0} => {1}  conf = {2}", a, b, confi);
                            AssiationRules.Add(r);
                        }
                    }
                }
            }
        }


        public void Simple_AR2(List<FrequentItem> fre)
        {
            AssiationRules = new List<string>();
            foreach (var fi in fre.FindAll(x => x.Name.Count >= 2))
            {
                var subfi = GetSubFi(fi.Name);
                foreach (var consequent in subfi.FindAll(x => x.Count == 1))
                {
                    var ante = fi.Name.Except(consequent).ToList();
                    var antecedent = FindFI(ante, fre);
                    double conf = fi.Supp / antecedent.Supp;
                    if (conf >= MinConf)
                    {
                        string a = "";
                        antecedent.Name.ForEach(x => a += x.AttName + ": " + x.Name + "   ");
                        string b = "";
                        consequent.ForEach(x => b += x.AttName + ": " + x.Name + "   ");
                        string r = String.Format("{0} => {1}  conf = {2}", a, b, conf);
                        AssiationRules.Add(r);
                    }
                }
            }
        }
        /// <summary>
        /// Tim fi trong danh sach cac fi
        /// </summary>
        /// <param name="an">tap can tim</param>
        /// <param name="frequent"></param>
        /// <returns></returns>
        private FrequentItem FindFI(List<TransactionValue> an, List<FrequentItem> frequent)
        {
            FrequentItem result = new FrequentItem();
            foreach (var item in frequent.FindAll(x => x.Name.Count == an.Count))
            {
                bool y = false;
                foreach (var i in item.Name)
                {
                    TransactionValue inew = new TransactionValue(i);
                    foreach (var a in an)
                    {
                        if (a.AttName == i.AttName && a.Name == i.Name)
                        {
                            result.Name.Add(inew);
                            y = true;
                        }
                    }
                }
                if (y == true)
                {
                    result.Supp = item.Supp;
                    break;
                }
            }
            return result;
        }


        List<TransactionValue> Getfi(FrequentItem fi, List<TransactionValue> name)
        {
            List<TransactionValue> result = new List<TransactionValue>();
            foreach (var i in name)
            {
                foreach (var it in fi.Name)
                {
                    if (i.Name != it.Name && i.AttName != it.AttName)
                        result.Add(it);
                }
            }
            return result;
        }

        /// <summary>
        /// Kiểm tra tập con
        /// </summary>
        /// <param name="child">Tập con</param>
        /// <param name="parent">Tâp cha</param>
        /// <returns></returns>
        private bool CheckSubset(List<TransactionValue> child, List<TransactionValue> parent)
        {
            if (child.Any(x => parent.Find(a => a.Name == x.Name && a.AttName == x.AttName) == null))
                return false;
            return true;
        }


    }
}
